﻿//
// $HeadURL: http://codeka-memcache.googlecode.com/svn/trunk/Codeka.Memcache/Commands/Packet.cs $
// $Revision: 5 $
// $Author: dean.codeka $     $Date: 2010-02-19 22:52:37 +0000 (Fri, 19 Feb 2010) $
//
// Copyright © 2009-2010 Dean Harding, all rights reserved.
//

using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.Net;

namespace Codeka.Memcache.Commands
{
    /// <summary>
    /// This is a generic <b>Packet</b> class an it encapsulates a memcache binary packet.
    /// </summary>
    public class Packet
    {
        public Packet()
        {
            Header = new PacketHeader();
        }

        /// <summary>
        /// Gets the <see cref="PacketHeader"/> which represents our header.
        /// </summary>
        public PacketHeader Header { get; private set; }

        /// <summary>
        /// Gets or sets the key associated with this <see cref="Command"/>
        /// </summary>
        public string Key { get; set; }

        /// <summary>
        /// Gets or sets the command-specific extras for this packet.
        /// </summary>
        public byte[] Extra { get; set; }

        /// <summary>
        /// Gets or sets the value associated with this <see cref="Packet"/>.
        /// </summary>
        public byte[] Value { get; set; }

        /// <summary>
        /// Serializes the contents of this <see cref="Packet"/> to the given <see cref="BinaryWriter"/>.
        /// </summary>
        public void Serialize(BinaryWriter writer)
        {
            // todo: make this conversion pluggable
            byte[] keyBytes;
            if (Key != null)
                keyBytes = Encoding.UTF8.GetBytes(Key);
            else
                keyBytes = new byte[0];

            // make sure the various bits of the header are properly set up
            // before we serialize it
            Header.ExtrasLength = (byte)((Extra == null) ? 0 : Extra.Length);
            Header.KeyLength = (byte)keyBytes.Length;
            Header.TotalBodyLength = Header.ExtrasLength + Header.KeyLength + (Value == null ? 0 : Value.Length);
            Header.DataType = 1;

            Header.Serialize(writer);
            if (Extra != null)
            {
                writer.Write(Extra);
            }
            writer.Write(keyBytes);
            if (Value != null)
            {
                writer.Write(Value);
            }
        }

        /// <summary>
        /// Deserializes a packet that we've received from the server.
        /// </summary>
        public void Deserialize(PacketHeader header, BinaryReader reader)
        {
            Header = header;

            if (Header.ExtrasLength == 0)
                Extra = null;
            else
                Extra = reader.ReadBytes(Header.ExtrasLength);

            if (Header.KeyLength == 0)
            {
                Key = null;
            }
            else
            {
                var keyBytes = reader.ReadBytes(Header.KeyLength);
                
                // todo: make this conversion pluggable
                Key = Encoding.UTF8.GetString(keyBytes);
            }

            int valueLength = Header.TotalBodyLength - Header.KeyLength - Header.ExtrasLength;
            if (valueLength == 0)
            {
                Value = null;
            }
            else
            {
                Value = reader.ReadBytes(valueLength);
            }
        }
    }
}
